#include "tile.h"

/**
 * 17.3.12 - Steven Hartin - Fixed constructors not intiialising entity position
 *  Added the ability to remove tile faces
 *  Automatically removes tile bottoms
 *  Properly constructs textures and the mesh
 *  Now uses our own tile.x file rather than the default box drawbox 
 *
 * 03/03/12 - Steven Hartin - Original Creation.
 *
 * Subsets:
 *    0 = left
 *    1 = bottom
 *    2 = top
 *    3 = front
 *    4 = right
 *    5 = back
 */

CTile::CTile(ENTITY_ID nTileID, short int nHeight, short int nWidth, 
               short int nDepth)
    : m_nTileID(nTileID),
      m_bDroppable(false),
      m_bWalkable(false),
      m_bDestructible(false),
      m_szTextureName(""),
      m_pPickup(nullptr),
      CMovableEntity("tile", nTileID, nWidth * Constants::TILE_SIZE, 
                                      nHeight * Constants::TILE_SIZE,
                                      nDepth * Constants::TILE_SIZE),
      m_nHeight(nHeight),
      m_nWidth(nWidth),
      m_nDepth(nDepth),
      m_bBeingDestroyed(false)
{
    /**
     * Constructor, ensure that we initialise the tile, and set up the default
     * attributes of any tile class. This class should never be instantiated
     * directly, instead, it should be subclassed by each individual tile type.
     *
     * @param ENTITY_ID nTileID The ID of the tile to create, usually overwrite
     *                          by any subclass.
     */
    setSubID(ENT_TILE_NONE);
    CMesh *pMesh(CMeshContainer::getInstance()->getMesh("tile"));
    if (pMesh == nullptr)
    {
        pMesh = CMeshContainer::getInstance()->addMesh("tile");
        pMesh->loadMesh(CDeviceManager::getInstance()->getDevice(), 
            "models/game/tile.x");
    }

    CDisplayable *pDisplayable(
        CDisplayableContainer::getInstance()->createDisplayable(pMesh));

    setDisplayable(pDisplayable);

    pDisplayable->setAutomaticallyRendered(false);

    CMaterial mat;

    //mat.setAmbient(0.45f, 0.45f, 0.45f, 0.0f);
    //mat.setDiffuse(0.45f, 0.45f, 0.45f, 0.0f);
    mat.setDiffuse(1.0f, 1.0f, 1.0f, 0.0f);
    mat.setAmbient(1.0f, 1.0f, 1.0f, 0.0f);

    pDisplayable->setMaterial(mat, 0);
    pDisplayable->setMaterial(mat, 1);
    pDisplayable->setMaterial(mat, 2);
    pDisplayable->setMaterial(mat, 3);
    pDisplayable->setMaterial(mat, 4);
    pDisplayable->setMaterial(mat, 5);

    CTexture *pTexture(
        CTextureContainer::getInstance()->getTexture("notexture"));
    if (pTexture == nullptr)
    {
        pTexture = CTextureContainer::getInstance()->addTexture("notexture",
                                    "textures/tiles/notexture.tga", 
                                    CDeviceManager::getInstance()->getDevice());
    }
    pDisplayable->setTexture(pTexture, 0);
    pDisplayable->setTexture(pTexture, 1);
    pDisplayable->setTexture(pTexture, 2);
    pDisplayable->setTexture(pTexture, 3);
    pDisplayable->setTexture(pTexture, 4);
    pDisplayable->setTexture(pTexture, 5);

    // Never render bottom of tiles.
    pDisplayable->setSubsetNonDrawable(TILE_FACE_BOTTOM);
}

CTile::CTile(ENTITY_ID nTileID, string szTextureName, short int nHeight,
             short int nWidth, short int nDepth)
    : m_nTileID(nTileID),
      m_bDroppable(false),
      m_bWalkable(false),
      m_bDestructible(false),
      m_pPickup(nullptr),
      m_szTextureName(szTextureName),
      CMovableEntity("tile", nTileID, nWidth * Constants::TILE_SIZE,
                                      nHeight * Constants::TILE_SIZE, 
                                      nDepth * Constants::TILE_SIZE),
      m_nHeight(nHeight),
      m_nWidth(nWidth),
      m_nDepth(nDepth),
      m_bBeingDestroyed(false)
{
    /**
     * Constructor, ensure that we initialise the tile, and set up the default
     * attributes of any tile class. This class should never be instantiated
     * directly, instead, it should be subclassed by each individual tile type.
     *
     * @param ENTITY_ID nTileID The ID of the tile to create, usually overwrite
     *                        by any subclass.
     * @param string szTextureName The texture to load
     * @param short int nHeight The height of the tile in the map.
     * @param short int nWidth The width of the tile in the map.
     * @param short int nDepth The depth of the tile in the map.
     */
    setSubID(ENT_TILE_NONE);
    CMesh *pMesh(CMeshContainer::getInstance()->getMesh("tile"));
    if (pMesh == nullptr)
    {
        pMesh = CMeshContainer::getInstance()->addMesh("tile");
        pMesh->loadMesh(CDeviceManager::getInstance()->getDevice(), 
            "models/game/tile.x");
    }

    setDisplayable(CDisplayableContainer::getInstance()->createDisplayable(
                   pMesh));

    CMaterial mat;

    mat.setDiffuse(1.0f, 1.0f, 1.0f, 0.0f);
    mat.setAmbient(1.0f, 1.0f, 1.0f, 0.0f);

    getDisplayable()->setMaterial(mat, 0);
    getDisplayable()->setMaterial(mat, 1);
    getDisplayable()->setMaterial(mat, 2);
    getDisplayable()->setMaterial(mat, 3);
    getDisplayable()->setMaterial(mat, 4);
    getDisplayable()->setMaterial(mat, 5);

    getDisplayable()->setAutomaticallyRendered(false);

    CTexture *pTexture(
        CTextureContainer::getInstance()->getTexture(szTextureName));
    if (pTexture == nullptr)
    {
        pTexture = CTextureContainer::getInstance()->addTexture(szTextureName,
                                    szTextureName, 
                                    CDeviceManager::getInstance()->getDevice());
    }
    getDisplayable()->setTexture(pTexture, 0);
    getDisplayable()->setTexture(pTexture, 1);
    getDisplayable()->setTexture(pTexture, 2);
    getDisplayable()->setTexture(pTexture, 3);
    getDisplayable()->setTexture(pTexture, 4);
    getDisplayable()->setTexture(pTexture, 5);

    // Never render bottom of tiles.
    getDisplayable()->setSubsetNonDrawable(TILE_FACE_BOTTOM);
}

void CTile::setDroppable(bool bDroppable)
{
    m_bDroppable = bDroppable;
}

bool CTile::isDroppable() const
{
    return m_bDroppable;
}

/*void CTile::setTimeToDrop(double dTimeToDropMilliseconds)
{
}

double CTile::getTimeToDrop() const
{
}*/

void CTile::setWalkable(bool bWalkable)
{
    m_bWalkable = bWalkable;
}

bool CTile::isWalkable() const
{
    return m_bWalkable;
}

short int CTile::getHeight(){
    /**
     * @return int The height position in the map.
     */ 
    return m_nHeight;
}

short int CTile::getWidth(){
    /**
     * @return int The width position in the map.
     */ 
    return m_nWidth;
}

short int CTile::getDepth(){
    /**
     * @return int The depth position in the map.
     */ 
    return m_nDepth;
}

void CTile::setHeight(int height){
    /**
     *
     * Sets the height of the tile.
     *
     * @param int The new height to set the height to.
     */ 
    m_nHeight = height;
    setY(m_nHeight * Constants::TILE_SIZE);
}

void CTile::setWidth(int width){
    /**
     *
     * Sets the width of the tile.
     *
     * @param int The new width to set the width to.
     */ 
    m_nWidth = width;
    setX(m_nWidth * Constants::TILE_SIZE);
}

void CTile::setDepth(int depth){
    /**
     *
     * Sets the depth of the tile.
     *
     * @param int The new depth to set the depth to.
     */ 
    m_nDepth = depth;
    setZ(m_nDepth * Constants::TILE_SIZE);
}

bool CTile::isDestructible() const
{
    /**
     * @return bool Whether the tile is destructible.
     */
    return m_bDestructible;
}

void CTile::setDestructible(bool bDestructible)
{
    /**
     * @param bool bDestructible Whether the tile is destructible
     */
    m_bDestructible = bDestructible;
}

bool CTile::isBeingDestroyed() const
{
    return m_bBeingDestroyed;
}

void CTile::setDestroyed(bool bValue)
{
    m_bBeingDestroyed = bValue;
}

void CTile::addEntityInBlock(CEntity *pEntity)
{
    /**
     * Adds an entity in the block. This sshould be called for any entitiy which
     * needs to be registered inside a block; i.e. projectiles or players.
     *
     * @param CEntity *pEntity The entity to store in the block
     */
    if (hasEntitiesInBlock())
    {
        removeEntityFromBlock(pEntity);
    }
    m_arrEntities.push_back(pEntity);
}

void CTile::removeEntityFromBlock(CEntity *pEntity)
{
    /**
     * Removes an entity if it exists within the block.
     *
     * @param CEntity *pEntity The entity to remove fromt he block.
     */
    if (hasEntitiesInBlock())
    {
        //m_arrEntities.erase(
        //    std::remove(m_arrEntities.begin(), m_arrEntities.end(), pEntity));
        for (vector<CEntity*>::const_iterator pPos(m_arrEntities.begin());
            pPos != m_arrEntities.end(); )
        {
            if (*pPos == pEntity)
            {
                pPos = m_arrEntities.erase(pPos);
                if (! m_arrEntities.size())
                {
                    break;
                }
            }
            else
            {
                ++pPos;
            }
        }
    }
}

bool CTile::hasEntitiesInBlock() const
{
    /**
     * @return bool WHether or not this tile has entities inside it.
     */
    return m_arrEntities.size() > 0;
}

size_t CTile::getEntityCount() const
{
    return m_arrEntities.size();
}

bool CTile::isEntityInBlock(CEntity *pEntity) const
{
    /**
     * @param CEntity *pEntity The entity to check if it's within the tile.
     * @return bool Whether the entity is currently within the block.
     */
    return std::find(m_arrEntities.begin(), m_arrEntities.end(), pEntity) !=
           m_arrEntities.end();
}

bool CTile::isEntityInBlock(const ENTITY_ID &nId) const
{
    /**
     * Tests to see if any entity with the ID is currently within the tile.
     *
     * @param const ENTITY_ID &nId THe ID to check for
     * @return bool Whether any entities with that ID are in the tile.
     */
    for (vector<CEntity*>::const_iterator pEntity(m_arrEntities.begin());
         pEntity != m_arrEntities.end(); ++pEntity)
    {
        if ((*pEntity)->getID() == nId)
        {
            return true;
        }
    }
    return false;
}

vector<CEntity*> CTile::getAllEntitiesInBlock() const
{
    /**
     * @return vector<CEntity*> A vector of all entities in this block
     */
    return m_arrEntities;
}

void CTile::tick()
{
    /**
     * Executed once every render loop.
     */
    if (hasEntitiesInBlock() || m_pPickup != nullptr)
    {
        getDisplayable()->showBoundingBox();
    }
    else
    {
        getDisplayable()->hideBoundingBox();
    }
}

void CTile::setPickup(CPickup *pPickup)
{
    /**
     * @param CPickup *pPickup The pickup to assign to this tile.
     */
    m_pPickup = pPickup;
}

CPickup *CTile::getPickup() const
{
    /**
     * @return CPickup* Any pickup associated with this tile; i.e. the one which
     *  will be dropped on destruction.
     */
    return m_pPickup;
}